/**
 * 
 */
package TelitPy;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

import javax.swing.Timer;

import COMM232.DriverSerial;

/**
 * @author Emerson Beserra.
 *
 */
public class BootLoader implements ActionListener
{
	
	/**
	 * STX - Start of Text.
	 */
	private final char beginBoot = 0x02;
	
	/**
	 * ETX - end of text.
	 */
	public static final byte endBoot = 0x03;
	
//	/**
//	 * EOT - End of Transmission.
//	 */
//	private final char endBoot   = 0x04;
	
	/**
	 * SOH - Start of Heading.
	 */
	private final char beginPack   = 0x01;
	
	/**
	 * ETB - End of Block.
	 */
	private final char endPack   = 0x17;
	
	private final char ACK = 0x06;
	
	private final char NACK = 0x15;
	
	private final char packLenght = 0xFF;
	
	private BufferedInputStream serialDataIn;
	
	private BufferedOutputStream serialDataOut;
	
	private DriverSerial driver;
	
	private BufferedReader fileIn;
	
	private Timer timer;
	
	private volatile boolean isTimeOut;
	
	public BootLoader(DriverSerial driver, String fileName)
	{
		int loop = 0;
		int count = 0;
		driver.disableSerialEvent();
		this.driver = driver;
		serialDataIn  = driver.getSerialDataIn();
		serialDataOut = driver.getSerialDataOut();
		readFile(fileName);
		timer = new Timer(3000, this);
		isTimeOut = false;
		timer.start();
		loop = 10;
		if( beginBoot() )
		{
			timer.restart();
			boot();
//			while(loop != 0)
//			{
//				loop--;
//				if(boot())
//				{
//					loop = 0;
//				}//end if
//				closeFile();
//				readFile(fileName);
//				count++;
//			}//end while
		}//end if
		timer.restart();
		endBoot();
		timer.stop();
		System.out.println("contador de erro: " + count);
		closeFile();
		driver.enableSerialEvent();
		
	}//end BootLoader()---------------------------------------------------------
	
	private void readFile(String fileName)
	{
		try 
		{
			fileIn = new BufferedReader(new FileReader(fileName));
		}
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}//end readFile()-----------------------------------------------------------
	
	private void closeFile()
	{
		try
		{
			fileIn.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}//end closeFile()----------------------------------------------------------
	
	private boolean boot()
	{
		boolean retVal = false;
		int state = 2;//estado inicial eh 2, zero eh estado de erro e 1 conclui com sucesso
		int previousState = 2;
		boolean loop = true;
		final int ERROR = 0;
		final int SUCESS = 1;
		char payload[] = new char[packLenght];
		int payloadIdx = 0;
		Packet packet = null;
		int c;
		boolean isAck = false;
		int failureCount = 0;
			
		while(loop)
		{
			if(state != 0)
				previousState = state;
			try
			{
				switch(state)
				{
					//erro
					case ERROR:
						retVal = false;
						loop = false;
						System.out.println("condicao de erro em boot no estado: " + previousState);
					break;
					//conclusao com sucesso
					case SUCESS:
						retVal = true;
						loop = false;
						System.out.println("Boot concluido com sucesso.");
					break;
					//inicio
					case 2:
						if( fileIn.ready() )
						{
							state++;//continua lendo
							payloadIdx = 0;
						}
						else
							state = SUCESS;//conclui com sucesso
					break;
					//leitura do arquivo e montagem do payload
					case 3:
						if( ( payloadIdx >= packLenght ) || ( !fileIn.ready() ) )
							state++;
						else
						{
							payload[payloadIdx] = (char) fileIn.read();
							++payloadIdx;
						}//end if-else
					break;
					//montagem do pacote
					case 4:
						packet = new Packet(payloadIdx, payload, driver);
						state++;
					break;
					//envio do pacote
					case 5:
						packet.sendPacket();
						state++;
						failureCount++;
					break;
					//espera pela resposta
					case 6:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 7:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//faz leitura de cabecalho
					case 8:
						c = serialDataIn.read();
						if( c == beginPack )
						{
							c = serialDataIn.read();
							if( c == beginPack )
							{
								state++;
							}
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 9:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 10:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le payload
					case 11:
						c = serialDataIn.read();
						if( c ==  ACK )
						{
							System.out.println("ACK");
							state++;
							isAck = true;
						}	
						else if( c ==  NACK )
						{
							System.out.println("NACK");
							state++;
							isAck = false;
						}
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 12:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 13:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le fim de pacote
					case 14:
						if( serialDataIn.read() == endPack )
						{
							if( serialDataIn.read() == endPack )
								state++;
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
					//verifica se o pacote foi lido com sucesso
					case 15:
						if(isAck)
						{
							System.out.println("isAck");
							state = 2;
							timer.restart();
							failureCount = 0;
						}//end if
						else
						{
							System.out.println("numero de nacks: " + failureCount);
							if( failureCount > 5 )
								state = ERROR;
							else
								state = 5;//reeenviar o pacote
						}
					break;
				}//end swicth
			}
			catch(IOException io)
			{
				io.printStackTrace();
			}
		}//end loop
		
		return retVal;
	}//end boot()---------------------------------------------------------------
	
//	private boolean boot2()
//	{
//		byte compare   = 0;
//		byte chRead    = 0;
//		boolean laco   = true;
//		boolean retVal = false;
//		int state      = 1;//estado inicial eh 1, zero eh estado de erro
//		int previousState = 0;
//		int ordCounter = 0;
//		
//		while(laco)
//		{
//			if(state != 0)
//				previousState = state;
//			
//			switch(state)
//			{
//				//erro
//				case 0:
//					retVal = false;
//					laco = false;
//					System.out.println("condicao de erro no estado: " + previousState);
//					//System.exit(0);
//				break;
//				//inicio
//				case 1:
//					try
//					{
//						if( fileIn.ready() )
//							state = 3;//continua lendo
//						else
//							state = 2;//conclui com sucesso
//					}
//					catch (IOException e)
//					{
//						e.printStackTrace();
//						state = 0;
//					}
//				break;
//				//conclui com sucesso
//				case 2:
//					System.out.println("fim");
//					retVal = true;
//					laco = false;
//				break;
//				//continua lendo
//				case 3:
//					try
//					{
//						compare = 0;
//						compare =  (byte) fileIn.read();
//						//compare = 'X';
//						state = 4;
//					}
//					catch (IOException e)
//					{
//						e.printStackTrace();
//						state = 0;
//					}
//				break;
//				//envia o byte
//				case 4:
//					try
//					{
//						serialDataOut.write(compare);
//						serialDataOut.flush();
//						state = 5;
//						System.out.println( (ordCounter++) + "_w " + Integer.toHexString((int) compare));
//					}
//					catch (IOException e)
//					{
//						e.printStackTrace();
//						state = 0;
//					}
//					
//				break;
//				//espera pela resposta
//				case 5:
//					try
//					{
//						if( serialDataIn.available() == 0 )
//							state = 5;
//						else
//							state = 6;
//					}
//					catch (IOException e)
//					{
//						e.printStackTrace();
//						state = 0;
//					}
//				break;
//				//le o byte
//				case 6:
//					try
//					{
//						chRead = 0;
//						chRead = (byte) serialDataIn.read();
//						state = 7;
//					}
//					catch (IOException e)
//					{
//						e.printStackTrace();
//						state = 0;
//					}
//				break;
//				//compara a resposta
//				case 7:
//					if( compare == chRead )
//						state = 8;
//					else
//						state = 0;
//					System.out.println("r " + Integer.toHexString((int) chRead));
//					try
//					{
//						timer.stop();
//						Thread.sleep(50);
//						timer.restart();
//					}
//					catch (InterruptedException e)
//					{
//						e.printStackTrace();
//					}
//				break;
//				//reinicia o timer e verifica se houve o time-out
//				case 8:
//					if( isTimeOut )
//						state = 0;
//					else
//					{
//						state = 1;
//						timer.restart();
//					}
//				break;
//			}//end switch
//		}//end while
//		return retVal;
//	}//end boot()---------------------------------------------------------------

	public void actionPerformed(ActionEvent e)
	{
		isTimeOut = true;
		timer.stop();
	}//end actionPerformed()----------------------------------------------------
	
	private boolean beginBoot()
	{
		boolean valRet = false;
		boolean loop = true;
		int state = 2;//estado inicial da maquina de estados
		int previousState = 2;
		final int ERROR = 0;
		final int SUCESS = 1;
		int c;
		
		while( loop )
		{
			if(state != 0)
				previousState = state;
			try
			{
				switch( state )
				{
					//erro
					case ERROR:
						valRet = false;
						loop = false;
						System.out.println("erro em beginBoot no estado: " + previousState);
					break;
					//finaliza com sucesso
					case SUCESS:
						valRet = true;
						loop = false;
						System.out.println("beginBoot concluido com sucesso");
					break;
					//inicia o boot
					case 2:
						serialDataOut.write('B');
						serialDataOut.write(0x0D);
						serialDataOut.write(beginBoot);
						serialDataOut.write(beginBoot);
						serialDataOut.flush();
						state++;
					break;
					//espera pela resposta
					case 3:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 4:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//faz leitura de cabecalho
					case 5:
						if( serialDataIn.read() == 'B' )
						{
							if( serialDataIn.read() == 0x0D )
							{
								if( serialDataIn.read() == 0x0A )
									state++;
								else
									state = ERROR;
							}//end if
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 6:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 7:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//faz leitura de cabecalho
					case 8:
						c = serialDataIn.read();
						if( c == beginPack )
						{
							c = serialDataIn.read();
							if( c == beginPack )
							{
								state++;
							}
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 9:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 10:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le payload
					case 11:
						if( serialDataIn.read() ==  ACK )
						{
							state++;
						}	
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 12:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 13:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le fim de pacote
					case 14:
						if( serialDataIn.read() == endPack )
						{
							if( serialDataIn.read() == endPack )
								state = SUCESS;
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
				}//end switch
			}
			catch(IOException io)
			{
				io.printStackTrace();
			}
		}//end while
		
		return valRet;
	}//end beginBoot()----------------------------------------------------------
	
	
	private boolean endBoot()
	{
		boolean valRet = false;
		boolean loop = true;
		int state = 2;//estado inicial da maquina de estados
		int previousState = 2;
		final int ERROR = 0;
		final int SUCESS = 1;
		int c;
		
		while( loop )
		{
			if(state != 0)
				previousState = state;
			try
			{
				switch( state )
				{
					//erro
					case ERROR:
						valRet = false;
						loop = false;
						System.out.println("erro em endBoot no estado: " + previousState);
					break;
					//finaliza com sucesso
					case SUCESS:
						valRet = true;
						loop = false;
						System.out.println("endBoot concluido com sucesso");
					break;
					//inicia o boot
					case 2:
						serialDataOut.write(endBoot);
						serialDataOut.write(endBoot);
						serialDataOut.flush();
						state++;
					break;
					//espera pela resposta
					case 3:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 4:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//faz leitura de cabecalho
					case 5:
						c = serialDataIn.read();
						if( c == beginPack )
						{
							c = serialDataIn.read();
							if( c == beginPack )
							{
								state++;
							}
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 6:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 7:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le payload
					case 8:
						if( serialDataIn.read() ==  ACK )
						{
							state++;
						}	
						else
							state = ERROR;
					break;
					//espera pela resposta
					case 9:
						if( serialDataIn.available() == 0 )
							state++;
						else
							state += 2;
					break;
					//verifica time-out
					case 10:
						if( isTimeOut )
						{
							state = ERROR;
							System.out.println("time-out");
						}
						else
							state--;
					break;
					//le fim de pacote
					case 11:
						if( serialDataIn.read() == endPack )
						{
							if( serialDataIn.read() == endPack )
								state = SUCESS;
							else
								state = ERROR;
						}//end if
						else
							state = ERROR;
					break;
				}//end switch
			}
			catch(IOException io)
			{
				io.printStackTrace();
			}
		}//end while
		
		return valRet;
	}//end endBoot()------------------------------------------------------------
	
//	private boolean beginBoot2( boolean isError )
//	{
//		int state = 1;//estado inicial da maquina de estados
//		boolean valRet = false;
//		boolean laco = true;
//		final char bootWord[] = {0x0D, 0x0A, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x4F, 0x4B, 0x21, 0x0D, 0x0A};
//		int idxWord = 0;
//		int previousState = 0;
//		
//		if(isError)
//			state = 9;
//		
//		while( laco )
//		{
//			if( state != 0 )
//				previousState = state;
//			try
//			{
//				switch ( state )
//				{
//					//erro
//					case 0:
//						valRet = false;
//						laco = false;
//						System.out.println("finalizado com erro no estado: " + previousState);
//					break;
//					//inicio
//					case 1:
//						serialDataOut.write( 'B' );
//						serialDataOut.write( 0x0D );
//						serialDataOut.flush();
//						state++;
//					break;
//					//verifica se houve resposta
//					case 2:
//						if( serialDataIn.available() == 0 )
//							state++;
//						else
//							state += 2;
//					break;
//					//verifica time-out
//					case 3:
//						if( isTimeOut )
//						{
//							state = 0;
//							System.out.println("time-out");
//						}
//						else
//							state--;
//					break;
//					//le dados
//					case 4:
//						if( serialDataIn.read() == 'B' )
//						{
//							if( serialDataIn.available() != 0 )
//								state++;
//							else
//								state = 0;
//						}//end if
//						else
//							state = 0;
//					break;
//					//le dados
//					case 5:
//						if( serialDataIn.read() == 0x0D )
//						{
//							if( serialDataIn.available() != 0 )
//								state++;
//							else
//								state = 0;
//						}//end if
//						else
//							state = 0;
//					break;
//					//le dados
//					case 6:
//						if( serialDataIn.read() == 0x0A )
//						{
//							if( serialDataIn.available() != 0 )
//								state++;
//							else
//								state = 0;
//						}//end if
//						else
//							state = 0;
//					break;
//					//le dados
//					case 7:
//						if( serialDataIn.available() != 0 )
//						{
//							if( serialDataIn.read() == bootWord[idxWord++] )
//							{
//								if( idxWord == bootWord.length )
//									state = 13;
//							}//end if
//							else
//								state = 0;
//						}//end if
//						else
//						{
//							if( idxWord > bootWord.length  )
//								state = 0;
//							else
//								state++;
//						}
//					break;
//					//espera mais dados
//					case 8:
//						if( serialDataIn.available() == 0 )
//						{
//							if( isTimeOut )
//								state = 0;
//						}//end if
//						else
//							state--;
//					break;
//					//escreve o inicializador de boot
//					case 9:
//						serialDataOut.write( '1' );
//						serialDataOut.write( '1' );
//						serialDataOut.flush();
//						state++;
//					break;
//					//espera recepcao
//					case 10:
//						if( serialDataIn.available() == 0 )
//							state++;
//						else
//							state += 2;
//					break;
//					//verifica time-out
//					case 11:
//						if( isTimeOut )
//						{
//							state = 0;
//							System.out.println("time-out");
//						}
//						else
//							state--;
//					break;
//					//le dados
//					case 12:
//						if( serialDataIn.read() == '1' )
//						{
//							if( serialDataIn.available() != 0 )
//								state = 7;
//							else
//								state = 0;
//						}//end if
//						else
//							state = 0;
//					break;
//					//finaliza com sucesso
//					case 13:
//						valRet = true;
//						laco = false;
//						//System.out.println("finalizado com sucesso");
//					break;
//				}//end swicth
//			}
//			catch( IOException e )
//			{
//				e.printStackTrace();
//				state = 0;
//			}
//		}//end while
//		return valRet;
//	}//end beginBoot------------------------------------------------------------
	
	/**
	 * 
	 * @return
	 */
//	private boolean endBoot()
//	{
//		int state = 1;//estado inicial da maquina de estados
//		boolean valRet = false;
//		boolean laco = true;
//		final char bootWord[] = {0x0D, 0x0A, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x4F, 0x4B, 0x21, 0x0D, 0x0A};
//		int idxWord = 0;
//		int previousState = 0;
//		
//		while( laco )
//		{
//			if( state != 0 )
//				previousState = state;
//			try
//			{
//				switch ( state )
//				{
//					//erro
//					case 0:
//						valRet = false;
//						laco = false;
//						System.out.println("finalizado com erro no estado: " + previousState);
//					break;
//					//inicio
//					case 1:
//						serialDataOut.write( '2' );
//						serialDataOut.write( '2' );
//						serialDataOut.flush();
//						state++;
//					break;
//					//espera recepcao
//					case 2:
//						if( serialDataIn.available() == 0 )
//							state++;
//						else
//							state += 2;
//					break;
//					//verifica time-out
//					case 3:
//						if( isTimeOut )
//						{
//							state = 0;
//							System.out.println("time-out");
//						}
//						else
//							state--;
//					break;
//					//le dados
//					case 4:
//						if( serialDataIn.read() == '2' )
//						{
//							if( serialDataIn.available() != 0 )
//								state = 6;
//							else
//								state = 0;
//						}//end if
//						else
//							state = 0;
//					break;
//					//finaliza com sucesso
//					case 5:
//						valRet = true;
//						laco = false;
//						System.out.println("finalizado com sucesso");
//					break;
//					//le dados
//					case 6:
//						if( serialDataIn.available() != 0 )
//						{
//							if( serialDataIn.read() == bootWord[idxWord++] )
//							{
//								if( idxWord == bootWord.length )
//									state = 8;
//							}//end if
//							else
//								state = 0;
//						}//end if
//						else
//						{
//							if( idxWord > bootWord.length  )
//								state = 0;
//							else
//								state++;
//						}
//					break;
//					//espera mais dados
//					case 7:
//						if( serialDataIn.available() == 0 )
//						{
//							if( isTimeOut )
//								state = 0;
//						}//end if
//						else
//							state--;
//					break;
//					//espera cursor
//					case 8:
//						if( serialDataIn.read() == '>' )
//							state = 5;
//						else
//							state = 0;
//					break;
//				}//end swicth
//			}
//			catch( IOException e )
//			{
//				e.printStackTrace();
//				state = 0;
//			}
//		}//end while
//		return valRet;
//	}//end endBoot()------------------------------------------------------------
	
}
